<?php
/**
 * API Response Optimization Middleware
 * Handles caching, compression, and response optimization
 */

namespace app\api\middleware;

use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
use support\Log;
use think\facade\Cache;
use think\facade\Config;

class ApiOptimizationMiddleware implements MiddlewareInterface
{
    /**
     * Process the request and optimize response
     */
    public function process(Request $request, callable $handler): Response
    {
        $startTime = microtime(true);
        
        // Check if request can be cached
        if ($this->isCacheable($request)) {
            $cacheKey = $this->getCacheKey($request);
            $cachedResponse = $this->getCachedResponse($cacheKey);
            
            if ($cachedResponse) {
                return $this->addOptimizationHeaders($cachedResponse, $startTime, true);
            }
        }
        
        // Process the request
        $response = $handler($request);
        
        // Cache the response if applicable
        if ($this->isCacheable($request) && $response->getStatusCode() === 200) {
            $this->cacheResponse($request, $response);
        }
        
        // Add optimization headers
        $response = $this->addOptimizationHeaders($response, $startTime, false);
        
        // Log slow requests
        $this->logSlowRequest($request, $startTime);
        
        // Update performance metrics
        $this->updatePerformanceMetrics($request, $response, $startTime);
        
        return $response;
    }
    
    /**
     * Check if request is cacheable
     */
    private function isCacheable(Request $request): bool
    {
        // Only cache GET requests
        if ($request->method() !== 'GET') {
            return false;
        }
        
        // Don't cache requests with certain parameters
        $nonCacheableParams = ['refresh', 'nocache', 'realtime'];
        foreach ($nonCacheableParams as $param) {
            if ($request->input($param)) {
                return false;
            }
        }
        
        // Don't cache certain endpoints
        $nonCacheablePaths = ['/api/auth/login', '/api/auth/logout', '/api/apps/create'];
        foreach ($nonCacheablePaths as $path) {
            if (strpos($request->path(), $path) !== false) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * Generate cache key for request
     */
    private function getCacheKey(Request $request): string
    {
        $params = $request->all();
        ksort($params); // Sort parameters for consistent key generation
        
        return 'api_response_' . md5($request->path() . serialize($params) . $request->getUri());
    }
    
    /**
     * Get cached response
     */
    private function getCachedResponse(string $cacheKey): ?Response
    {
        $cached = cache($cacheKey);
        if ($cached && isset($cached['body'], $cached['headers'], $cached['status'])) {
            return new Response($cached['status'], $cached['headers'], $cached['body']);
        }
        return null;
    }
    
    /**
     * Cache the response
     */
    private function cacheResponse(Request $request, Response $response): void
    {
        $cacheKey = $this->getCacheKey($request);
        $ttl = $this->getCacheTtl($request);
        
        $data = [
            'body' => $response->rawBody(),
            'headers' => $response->getHeaders(),
            'status' => $response->getStatusCode()
        ];
        
        cache($cacheKey, $data, $ttl);
    }
    
    /**
     * Get appropriate cache TTL based on request
     */
    private function getCacheTtl(Request $request): int
    {
        $config = config('security.optimization', []);
        
        // Short TTL for dynamic data
        if (strpos($request->path(), '/stats') !== false || strpos($request->path(), '/statistics') !== false) {
            return $config['cache_ttl']['stats'] ?? 300; // 5 minutes
        }
        
        // Medium TTL for app lists
        if (strpos($request->path(), '/apps') !== false) {
            return $config['cache_ttl']['apps'] ?? 900; // 15 minutes
        }
        
        // Default TTL
        return $config['cache_ttl']['default'] ?? 600; // 10 minutes
    }
    
    /**
     * Add optimization headers to response
     */
    private function addOptimizationHeaders(Response $response, float $startTime, bool $fromCache): Response
    {
        $processingTime = round((microtime(true) - $startTime) * 1000, 2);
        
        $headers = [
            'X-Response-Time' => $processingTime . 'ms',
            'X-Cache' => $fromCache ? 'HIT' : 'MISS',
            'X-Optimization' => 'enabled'
        ];
        
        // Add compression header if response is large
        if (strlen($response->rawBody()) > 1024) {
            $headers['Content-Encoding'] = 'gzip';
        }
        
        foreach ($headers as $key => $value) {
            $response->withHeader($key, $value);
        }
        
        return $response;
    }
    
    /**
     * Log slow requests for monitoring
     */
    private function logSlowRequest(Request $request, float $startTime): void
    {
        $config = config('security.optimization', []);
        $slowThreshold = $config['slow_request_threshold'] ?? 2.0;
        
        $processingTime = microtime(true) - $startTime;
        
        if ($processingTime > $slowThreshold) {
            $logData = [
                'method' => $request->method(),
                'path' => $request->path(),
                'processing_time' => round($processingTime, 3),
                'timestamp' => date('Y-m-d H:i:s'),
                'user_agent' => $request->header('User-Agent', 'unknown')
            ];
            
            Log::warning('Slow API request detected', $logData);
        }
    }
    
    /**
     * Update performance metrics in cache
     */
    private function updatePerformanceMetrics(Request $request, Response $response, float $startTime): void
    {
        $processingTime = round((microtime(true) - $startTime) * 1000, 2); // Convert to milliseconds
        $statusCode = $response->getStatusCode();
        
        // Get current metrics
        $metrics = Cache::get('api_performance_metrics', [
            'total_requests' => 0,
            'error_requests' => 0,
            'total_response_time' => 0,
            'slow_requests' => 0,
            'last_updated' => time()
        ]);
        
        // Update metrics
        $metrics['total_requests']++;
        $metrics['total_response_time'] += $processingTime;
        
        if ($statusCode >= 400) {
            $metrics['error_requests']++;
        }
        
        if ($processingTime > 1000) { // Slow if > 1 second
            $metrics['slow_requests']++;
        }
        
        $metrics['last_updated'] = time();
        
        // Store updated metrics with configured TTL
        $config = config('security.performance', []);
        $ttl = $config['metrics_ttl'] ?? 3600;
        Cache::set('api_performance_metrics', $metrics, $ttl);
    }
}